#include "dicom/handler/handler_common.h"
#include "dicom/upload/cloud_login.h"
#include <json/json.h>
#include <gwbase/base/logging.h>

namespace dicom {

Status CheckToken(onion_request* conn, CredentialPtr* cred,
                  std::string* token) {
  Status s;
  const char* kPrefix = "Bearer ";
  const char* header = NULL;
  if ((header = onion_request_get_header(conn, "Authorization")) == NULL)
    return Status(Status::kOther, "token is invalid");

  std::string v(header);
  if (v.size() <= strlen(kPrefix) ||
      ::memcmp(v.c_str(), kPrefix, strlen(kPrefix) != 0)) {
    return Status(Status::kOther, "token is invalid");
  }

  std::string t = v.substr(::strlen(kPrefix));
  LOG_TRACE << "TOKEN: " << token;
  if (t.empty()) {
    return Status(Status::kOther, "token is empty");
  }

  CredentialPtr c = GetGateway()->session_mgr()->GetUserCredential(t);
  if (c == NULL) {
    LOG_ERROR << "credential is null";
    return Status(Status::kOther, "token is invalid");
  }

  // This token is loaded from database, we must login to server to
  // refresh token
  if (c->surdoc_key == -1) {
    const DicomConf* conf = GetGateway()->conf();
    AccessToken token;
    std::string root_dir_id;
    int64_t token_key;
    s = LoginToCloud(conf->GetCloudServer(), c->email, c->password, &token,
                     &token_key, &root_dir_id);
    if (!s.ok()) {
      return s;
    }

    GetGateway()->session_mgr()->Save(t, token.access_token, c->email,
                                      c->password, c->ukey_id, root_dir_id,
                                      token.privatekey, token_key);
    GetGateway()->sync_mgr()->AddNewUser(c->email);
    (*cred) = GetGateway()->session_mgr()->GetUserCredential(t);
  } else {
    *cred = c;
  }

  if (token != NULL) token->assign(t);

  LOG_TRACE << "email: " << (*cred)->email;
  return Status();
}

void SendSimpleResponse(onion_response* resp, int result_code,
                        const std::string& msg) {
  Json::Value v;
  v["code"] = result_code;
  v["message"] = msg;

  SendJsonResult(resp, result_code, v);
}

void SendJsonResult(onion_response* resp, int http_code, Json::Value& v) {
  ::onion_response_set_code(resp, http_code);
  const std::string str = v.toStyledString();
  ::onion_response_set_header(resp, "Content-Type", "application/json");
  ssize_t n = ::onion_response_write(resp, str.c_str(), str.length());
  assert(n >= 0 && static_cast<size_t>(n) == str.length());
  if (http_code != 200) {
    LOG_ERROR << "SendJsonResult: \n" << str;
  } else {
    LOG_DEBUG << "SendJsonResult: \n" << str;
  }
}

std::string GetQueryString(onion_request* req, const char* name) {
  const char* v = ::onion_request_get_query(req, name);
  if (v == NULL) return "";
  return v;
}

int64_t GetQueryStringInt64(onion_request* req, const char* name,
                            int64_t default_value) {
  std::string v = GetQueryString(req, name);
  if (v.empty()) return default_value;

  return ::strtoll(v.c_str(), NULL, 10);
}

std::string GetReqPath(onion_request* req) {
  const char* v = ::onion_request_get_path(req);
  return std::string("/") + v;
}

int GetReqMethod(onion_request* req) {
  return ::onion_request_get_flags(req) & 0x0F;
}

std::string GetReqMethodStr(onion_request* req) {
  return ::onion_request_methods[GetReqMethod(req)];
}
}
